#include "genericncctemplatematching.h"
#include "commonutils.h"
#include "AVL.h"
#include "STD.h"

GenericNCCTemplateMatching::GenericNCCTemplateMatching()
{

}

bool GenericNCCTemplateMatching::PR_Generic_NCC_Template_Matching(QImage image, QImage &resultImage, QString prName, PRResultStruct &prResult, double object_score, bool detect_small_hole, PRResultImageInfo &prResultImageInfo)
{
    if (!checkFileExist(prName)) return false;

    QString pr_offset_name = prName;
    QString pr_region_name = prName;
    QString pr_small_circle_name = prName;
    pr_offset_name.replace(".avdata", "_offset.avdata");
    pr_region_name.replace(".avdata", "_searchRegion.avdata");
    pr_small_circle_name.replace(".avdata", "_smallCircle.avdata");

    try {
        atl::String g_constData1;
        atl::String g_constData2;
        atl::String g_constData3;
        atl::String g_constData4;
        atl::String g_constData5;
        atl::String g_constData6;
        atl::String g_emptyString;
        atl::Array< atl::Conditional< avl::Location > > g_constData7;
        atl::String g_constData8;
        atl::String	g_constData9;
        atl::String g_constData10;
        atl::String g_constData11;
        atl::String g_constData12;

        g_constData1 = L"test.jpg";
        g_constData2 = pr_offset_name.toStdString().c_str();
        g_constData3 = L"Vector2D";
        g_constData4 = prName.toStdString().c_str();
        g_constData5 = L"GrayModel";
        g_constData6 = L"Angle:";
        g_constData8 = pr_region_name.toStdString().c_str();
        g_constData9 = L"Region";
        g_constData10 = L" Object:";
        g_constData11 = L"CircleFittingField";
        g_constData12 = pr_small_circle_name.toStdString().c_str();
        g_emptyString = L"";

        g_constData7.Reset(1);
        g_constData7[0] = avl::Location(253, 50);
        avl::Image image2;
        avl::Region region1;
        avl::CircleFittingField circleFittingField1;
        avl::Vector2D vector2D1;
        avl::GrayModel grayModel1;
        atl::Conditional< avl::Object2D > object2D1;
        atl::Conditional< avl::Point2D > point2D1;
        atl::Conditional< avl::Point2D > point2D2;
        atl::Conditional< atl::String > string1;
        atl::Conditional< avl::Rectangle2D > rectangle2D1;
        atl::String string2;
        atl::String string3;
        atl::Array< atl::Conditional< atl::String > > stringArray1;
        avl::Image image3;
        avl::Image image4;
        avl::Image image5;
        avl::Image image6;
        avl::Image image7;
        avl::Image image8;
        atl::Array< atl::Conditional< avl::Region > > regionArray1;
        avs::FitCircleToEdgesState fitCircleToEdgesState1;
        atl::Conditional< avl::Circle2D > circle2D1;

        avl::Image image1;
        image = image.convertToFormat(QImage::Format_RGB888);
        avl::Image imageInput(image.width(), image.height(), image.bytesPerLine(), avl::PlainType::Type::UInt8, image.depth() / 8, image.bits());
        avl::RealignImagePitch(imageInput, image1);

        avs::LoadObject< avl::Vector2D >( g_constData2, avl::StreamMode::Binary, g_constData3, vector2D1 );
        avs::LoadObject< avl::GrayModel >( g_constData4, avl::StreamMode::Binary, g_constData5, grayModel1 );
        avs::LoadObject< avl::Region >( g_constData8, avl::StreamMode::Binary, g_constData9, region1 );

        QFileInfo fileInfo(pr_small_circle_name);
        if(fileInfo.isFile())
        {
            qInfo("Load circle fitting field");
            avs::LoadObject< avl::CircleFittingField >( g_constData12, avl::StreamMode::Binary, g_constData11, circleFittingField1 );
        }

        avl::LocateSingleObject_NCC( image1, region1, grayModel1, 0, 3, false, 0.3f, object2D1, atl::NIL, atl::Dummy< atl::Array< avl::Image > >().Get(), atl::Dummy< atl::Array< avl::Image > >().Get(), atl::Dummy< atl::Conditional< atl::Array< float > > >().Get() );
        bool is_object_score_pass = true;
        if (object2D1 != atl::NIL)
        {
            avl::CoordinateSystem2D coordinateSystem2D1;
            float real1;
            avl::Point2D point2D3;
            float real2;
            float real3;
            float real4;

            point2D1.AssignNonNil();
            point2D2.AssignNonNil();
            string1.AssignNonNil();
            rectangle2D1.AssignNonNil();
            point2D1.Get() = object2D1.Get().Point();
            coordinateSystem2D1 = avl::CoordinateSystem2D(point2D1.Get(), 0.0f, 1.0f);
            avs::AvsFilter_FitCircleToEdges( fitCircleToEdgesState1, image1, circleFittingField1, coordinateSystem2D1, 10, 5, avl::InterpolationMethod::Bilinear, avl::EdgeScanParams(avl::ProfileInterpolationMethod::Quadratic4, 1.0f, 10.0f, avl::EdgeTransition::Any), avl::Selection::Best, atl::NIL, 0.1f, avl::CircleFittingMethod::AlgebraicTaubin, atl::NIL, circle2D1, atl::NIL, atl::NIL, atl::NIL, atl::Dummy< atl::Array< avl::Segment2D > >().Get(), atl::Dummy< atl::Array< avl::Rectangle2D > >().Get(), atl::Dummy< atl::Array< avl::Profile > >().Get(), atl::Dummy< atl::Array< avl::Profile > >().Get() );
            avl::TranslatePoint( point2D1.Get(), vector2D1, false, point2D2.Get() );
            real1 = object2D1.Get().Angle();
            avl::RealToString( real1, string2 );

            // AvsFilter_ConcatenateStrings is intended for generated code only. In regular programs  String::operator+() or String:Append() member function should be used.
            point2D3 = object2D1.Get().Match().Origin();
            real2 = object2D1.Get().Match().Width();
            real3 = object2D1.Get().Match().Height();
            real4 = object2D1.Get().Score();

            avl::RealToString( real4, string3 );
            avs::AvsFilter_ConcatenateStrings( g_constData6, string2, g_constData10, string3, g_emptyString, g_emptyString, g_emptyString, g_emptyString, string1.Get() );

            // Function AvsFilter_MakeRectangle is intended for generated code only. Consider use of proper Rectangle2D constructor instead.
            avs::AvsFilter_MakeRectangle( point2D3, real1, real2, real3, rectangle2D1.Get() );
            avl::RotatePoint(point2D2.Get(), point2D1.Get(), real1, false, point2D2.Get());
            prResult.ori_x = point2D1.Get().x;
            prResult.ori_y = point2D1.Get().y;
            prResult.x = point2D2.Get().x;
            prResult.y = point2D2.Get().y;
            prResult.theta = real1;
            prResult.width = object2D1.Get().Match().Width();
            prResult.height = object2D1.Get().Match().Height();

            qInfo("Object score: %f", object2D1.Get().score);
            if (circle2D1 != atl::NIL) {
                qInfo("Detected small hole at x: %f y: %f", circle2D1.Get().Center().X(), circle2D1.Get().Center().Y());
            } else {
                if (detect_small_hole) {
                    qWarning("Cannot find the small hole");
                }
            }
        }
        else
        {
            point2D1 = atl::NIL;
            point2D2 = atl::NIL;
            string1 = atl::NIL;
            rectangle2D1 = atl::NIL;
            circle2D1 = atl::NIL;
            qWarning("PR Error! Object Not Found");
            return false;
        }

        //Draw Result

        prResultImageInfo.rect_x = rectangle2D1.Get().X();
        prResultImageInfo.rect_y = rectangle2D1.Get().Y();

        prResultImageInfo.rect_angle = rectangle2D1.Get().Angle();

        prResultImageInfo.rect_ori_x = rectangle2D1.Get().Origin().X();
        prResultImageInfo.rect_ori_y = rectangle2D1.Get().Origin().Y();

        prResultImageInfo.rect_width = rectangle2D1.Get().Width();
        prResultImageInfo.rect_height = rectangle2D1.Get().Height();

        prResultImageInfo.result_ori_x = point2D1.Get().X();
        prResultImageInfo.result_ori_y = point2D1.Get().Y();

        prResultImageInfo.result_x = point2D2.Get().X();
        prResultImageInfo.result_y = point2D2.Get().Y();

        prResultImageInfo.resultString = QString("S: ").append("").append(QString(" A: ")).append("");

//        stringArray1.Resize(1);
//        stringArray1[0] = string1;
//        if (is_object_score_pass)
//            avs::DrawStrings_SingleColor( image1, stringArray1, g_constData7, atl::NIL, avl::Anchor2D::MiddleCenter, avl::Pixel(0.0f, 255.0f, 0.0f, 0.0f), avl::DrawingStyle(avl::DrawingMode::HighQuality, 1.0f, 1.0f, false, atl::NIL, 40.0f), 38.0f, 0.0f, true, atl::NIL, image3 );
//        else {
//            avs::DrawStrings_SingleColor( image1, stringArray1, g_constData7, atl::NIL, avl::Anchor2D::MiddleCenter, avl::Pixel(255.0f, 0.0f, 0.0f, 0.0f), avl::DrawingStyle(avl::DrawingMode::HighQuality, 1.0f, 1.0f, false, atl::NIL, 40.0f), 38.0f, 0.0f, true, atl::NIL, image3 );
//        }

//        avs::DrawPoints_SingleColor( image3, atl::ToArray< atl::Conditional< avl::Point2D > >(point2D2), atl::NIL, avl::Pixel(255.0f, 115.0f, 251.0f, 0.0f), avl::DrawingStyle(avl::DrawingMode::HighQuality, 1.0f, 4.0f, false, avl::PointShape::Cross, 40.0f), true, image4 );
//        avs::DrawPoints_SingleColor( image4, atl::ToArray< atl::Conditional< avl::Point2D > >(point2D1), atl::NIL, avl::Pixel(0.0f, 255.0f, 0.0f, 0.0f), avl::DrawingStyle(avl::DrawingMode::HighQuality, 1.0f, 4.0f, false, avl::PointShape::Cross, 40.0f), true, image5 );
//        avs::DrawRectangles_SingleColor( image5, atl::ToArray< atl::Conditional< avl::Rectangle2D > >(rectangle2D1), atl::NIL, avl::Pixel(255.0f, 255.0f, 0.0f, 0.0f), avl::DrawingStyle(avl::DrawingMode::HighQuality, 1.0f, 2.0f, false, atl::NIL, 1.0f), true, image6 );
//        regionArray1.Resize(1);
//        regionArray1[0].AssignNonNil();
//        regionArray1[0].Get() = region1;
//        avs::DrawRegions_SingleColor( image6, regionArray1, atl::NIL, avl::Pixel(192.0f, 255.0f, 192.0f, 0.0f), 0.3f, true, image7 );
//        avs::DrawCircles_SingleColor( image7, atl::ToArray< atl::Conditional< avl::Circle2D > >(circle2D1), atl::NIL, avl::Pixel(255.0f, 0.0f, 0.0f, 0.0f), avl::DrawingStyle(avl::DrawingMode::HighQuality, 1.0f, 1.0f, true, atl::NIL, 20.0f), true, image8 );
//        QImage testImage((uchar*)image8.Data(), image8.Width(), image8.Height(), image8.Pitch(), QImage::Format_RGB888);
//        resultImage = testImage.copy();
    }catch(const atl::Error& error) {
        qWarning("Throw Error: %s", error.Message());
        return false;
    }
    return true;
}
